#include "libclangmm/clangmm.hpp"
#include <string>
#include <memory>
#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#include <unordered_set>
#include <set>
#include <unordered_map>

using namespace std;

bool debug = true;
bool debugFunc = true;

class Project {
public:
    std::string m_projectName;
    std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>> m_funcInArgs;
    std::unordered_map<std::string, std::pair<std::string, std::string>> m_funcReturnTypeAndName;
};

Project project;
std::vector<std::string> declarings;

bool getIfStmt(const clangmm::Cursor &funcCursor, clangmm::Cursor &compoundStmtCursor)
{
    std::vector<clangmm::Cursor> child = funcCursor.get_children();
    auto it = find_if(child.cbegin(), child.cend(), [](clangmm::Cursor cursor){
        return cursor.get_kind() == clangmm::Cursor::Kind::IfStmt;
    });
    compoundStmtCursor = *it;
    return it != child.cend();
}

std::string getIndent()
{
    return "    ";
}

void makeSnapshot(const std::vector<std::pair<std::string, std::string>> &outterVarVec, const clangmm::Cursor &funcDecl)
{
}

void makeOutStruct(const std::vector<std::pair<std::string, std::string>> &outterVarVec, const clangmm::Cursor &funcDecl)
{
    std::string funcName = funcDecl.get_spelling();
    std::string structName = "outter_of_" + funcName;
    std::string beforeSnapVar = structName + "_before_execution";
    std::string afterSnapVar = structName + "_after_execution";
    std::string beforeSnap = "{\n" + getIndent() + structName + " " + beforeSnapVar + ";\n";
    std::string afterSnap = "{\n" + getIndent() + structName + " " + afterSnapVar + ";\n";
    std::string structDecl = "struct " + structName + " {\n";
    for(const auto &outterVar : outterVarVec) {
        structDecl += getIndent() + outterVar.first + " " + outterVar.second + ";\n";
        beforeSnap += getIndent() + beforeSnapVar + "." + outterVar.second + " = " + outterVar.second + ";\n";
        afterSnap += getIndent() + afterSnapVar + "." + outterVar.second + " = " + outterVar.second + ";\n";
    }
    structDecl += "};\n";
    beforeSnap += "}\n";
    afterSnap += "}\n";
    debugFunc && cout << structDecl << endl;
    debugFunc && cout << beforeSnap << endl;
    debugFunc && cout << afterSnap << endl;
}

void procVarDecl(const std::vector<clangmm::Cursor> &varDeclVec)// const 后面加const是成员函数才有的！！
{
}

void procDeclRefExpr(const std::vector<clangmm::Cursor> &declRefExprVec)
{
}

void procFuncBody(const clangmm::Cursor &compoundStmtCursor, const clangmm::Cursor &funcDecl)
{
    // debugFunc && cout << endl << "------------------ procFuncBody()" << endl;
    vector<clangmm::Cursor> callExprVec;
    // vector<clangmm::Cursor> declStmtVec;
    vector<clangmm::Cursor> varDeclVec;
    vector<clangmm::Cursor> declRefExprVec;
    queue<clangmm::Cursor> q;
    q.emplace(compoundStmtCursor);
    static int index = 0;
    while(!q.empty()) {
        clangmm::Cursor curFront = q.front();
        q.pop();
        
        auto curKind = curFront.get_kind();
        if(curKind == clangmm::Cursor::Kind::CallExpr) {
            callExprVec.emplace_back(curFront);
        } else if(curKind == clangmm::Cursor::Kind::VarDecl) {
            varDeclVec.emplace_back(curFront);
        } else if(curKind == clangmm::Cursor::Kind::DeclRefExpr) {
            declRefExprVec.emplace_back(curFront);
        }

        for(auto curTmp : curFront.get_children()) {
            if(curTmp.get_kind() == clangmm::Cursor::Kind::CallExpr) {
                continue;
            }
            q.emplace(curTmp);
        }
    }

    std::unordered_set<std::string> autoVarSet;
    std::vector<std::pair<std::string, std::string>> outterVarVec;

    auto args = funcDecl.get_arguments();
    debugFunc && cout << "arguments:" << endl;
    for(auto arg : args) {
        auto typeName = arg.get_type_description();
        auto varName = arg.get_spelling();
        debugFunc && cout << getIndent() << typeName + " " + varName + ";"<< endl;
        autoVarSet.emplace(varName);
    }

    // procVarDecl(varDeclVec);
    // procDeclRefExpr(declRefExprVec);
    debugFunc && cout << "procVarDecl:" << endl;
    for(const auto &varDecl : varDeclVec) {
        std::string typeName = varDecl.get_type_description();
        std::string varName = varDecl.get_spelling();
        debugFunc && cout << getIndent() << typeName << " " << varName << ";" << endl;
        autoVarSet.emplace(varName);
    }

    debugFunc && cout << "procDeclRefExpr:" << endl;
    for(const auto &declRefExpr : declRefExprVec) {
        std::string typeName = declRefExpr.get_type_description();
        std::string varName = declRefExpr.get_spelling();
        debugFunc && cout << getIndent() << typeName << " " << varName << ";" << endl;
        if(autoVarSet.cend() == autoVarSet.find(varName)) {
            outterVarVec.emplace_back(std::make_pair(typeName, varName));
        }
    }

    if(!outterVarVec.empty()) {
        debugFunc && cout << "outter varriable used in func:" << endl;
        for(const auto &outterVar : outterVarVec) {
            debugFunc && cout << getIndent() << outterVar.first << " " << outterVar.second << ";" << endl;
        }
        makeOutStruct(outterVarVec, funcDecl);
    }
}

bool getCompoundStmt(const clangmm::Cursor &funcCursor, clangmm::Cursor &compoundStmtCursor)
{
    std::vector<clangmm::Cursor> child = funcCursor.get_children();
    auto it = find_if(child.cbegin(), child.cend(), [](clangmm::Cursor cursor){
        return cursor.get_kind() == clangmm::Cursor::Kind::CompoundStmt;
    });
    compoundStmtCursor = *it;
    return it != child.cend();
}

void procFuncDecl(const clangmm::Cursor &funcDecl)
{
    debugFunc && cout << endl << "------------------ procFuncDecl()" << endl;
    std::string returnType = clangmm::Cursor::Type(clang_getResultType(funcDecl.get_type().cx_type)).get_spelling();
    std::string funcName = funcDecl.get_token_spelling();
    std::string funcDeclaring = returnType + " " + funcDecl.get_display_name() + ";";
    if(project.m_funcInArgs.cend() != project.m_funcInArgs.find(funcDeclaring)) {
        return;
    }
    debugFunc && cout << "funcDecl: " << funcDeclaring << endl;
    project.m_funcInArgs[funcDeclaring] = {};
    project.m_funcReturnTypeAndName[funcDeclaring] = std::make_pair(returnType, funcName);

    clangmm::Cursor compoundStmtCursor;
    if(getCompoundStmt(funcDecl, compoundStmtCursor)) {
        procFuncBody(compoundStmtCursor, funcDecl);
    }
}

int main()
{
    // clang++ -fsyntax-only -Xclang -ast-dump moc_file.cc
    vector<clangmm::Cursor> funcDeclVec;
    // vector<clangmm::Cursor> compoundStmtVec;
    std::string filepath = "oracle_file.cc";
    project.m_projectName = filepath;

    clangmm::TranslationUnit tu(std::make_shared<clangmm::Index>(0, 0), filepath, {});
    clangmm::Cursor cursor = tu.get_cursor();

    queue<clangmm::Cursor> q; // AST并不是严格的树形结构，其中也会有回路，所以需要找替代方案，加个标记
    q.emplace(cursor);
    while(!q.empty()) {
        clangmm::Cursor curFront = q.front();
        q.pop();
        // debug && cout << curFront.get_token_spelling() << endl;
        // debug && cout << "Path: " << curFront.get_source_location().get_path() << endl;

        if(curFront.get_source_location().get_path() == filepath) {
            // debug && cout << curFront.get_kind_spelling() << endl;
            auto curKind = curFront.get_kind();
            if(curKind == clangmm::Cursor::Kind::FunctionDecl) {
                funcDeclVec.emplace_back(curFront);
            }
        }

        for(const auto &curTmp : curFront.get_children()) {
            q.emplace(curTmp);
        }
    }

    for(const auto &funcDecl : funcDeclVec) {
        procFuncDecl(funcDecl);
    }

    return 0;
}